#ifndef SSDB_UTILS_H_
#define SSDB_UTILS_H_

#include <algorithm>
#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>

namespace ssdb {

inline static int is_empty_str(const char *str) {
  const char *p = str;
  while (*p && isspace(*p)) {
    p++;
  }
  return *p == '\0';
}

/* 返回左边不包含空白字符的字符串的指针 */
inline static char *ltrim(const char *str) {
  const char *p = str;
  while (*p && isspace(*p)) {
    p++;
  }
  return (char *)p;
}

/* 返回指向字符串结尾的指针, 会修改字符串内容 */
inline static char *rtrim(char *str) {
  char *p;
  p = str + strlen(str) - 1;
  while (p >= str && isspace(*p)) {
    p--;
  }
  *(++p) = '\0';
  return p;
}

/* 返回左边不包含空白字符的字符串的指针 */
inline static char *trim(char *str) {
  char *p;
  p = ltrim(str);
  rtrim(p);
  return p;
}

inline static void strtolower(std::string *str) {
  std::transform(str->begin(), str->end(), str->begin(), ::tolower);
}

inline static void strtoupper(std::string *str) {
  std::transform(str->begin(), str->end(), str->begin(), ::toupper);
}

// inline static
// std::string real_dirname(const char *filepath){
// 	std::string dir;
// 	if(filepath[0] != '/'){
// 		char buf[1024];
// 		char *p = getcwd(buf, sizeof(buf));
// 		if(p != NULL){
// 			dir.append(p);
// 		}
// 		dir.append("/");
// 	}

// 	const char *p = strrchr(filepath, '/');
// 	if(p != NULL){
// 		dir.append(filepath, p - filepath);
// 	}
// 	return dir;
// }

inline static void str_escape(const char *s, int size, std::string *ret) {
  static const char *hex = "0123456789abcdef";
  for (int i = 0; i < size; i++) {
    char c = s[i];
    switch (c) {
    case '\r':
      ret->append("\\r");
      break;
    case '\n':
      ret->append("\\n");
      break;
    case '\t':
      ret->append("\\t");
      break;
    case '\\':
      ret->append("\\\\");
      break;
    case ' ':
      ret->push_back(c);
      break;
    default:
      if (c >= '!' && c <= '~') {
        ret->push_back(c);
      } else {
        ret->append("\\x");
        unsigned char d = c;
        ret->push_back(hex[d >> 4]);
        ret->push_back(hex[d & 0x0f]);
      }
      break;
    }
  }
}

inline static std::string str_escape(const char *s, int size) {
  std::string ret;
  int capacity = (size < 20) ? 32 : (size * 1.2);
  ret.reserve(capacity);
  str_escape(s, size, &ret);
  return ret;
}

inline static std::string str_escape(const std::string &s) {
  return str_escape(s.data(), (int)s.size());
}

inline static int hex_int(char c) {
  if (c >= '0' && c <= '9') {
    return c - '0';
  } else {
    return c - 'a' + 10;
  }
}

inline static void str_unescape(const char *s, int size, std::string *ret) {
  for (int i = 0; i < size; i++) {
    char c = s[i];
    if (c != '\\') {
      ret->push_back(c);
    } else {
      if (i >= size - 1) {
        continue;
      }
      char c2 = s[++i];
      switch (c2) {
      case 'a':
        ret->push_back('\a');
        break;
      case 'b':
        ret->push_back('\b');
        break;
      case 'f':
        ret->push_back('\f');
        break;
      case 'v':
        ret->push_back('\v');
        break;
      case 'r':
        ret->push_back('\r');
        break;
      case 'n':
        ret->push_back('\n');
        break;
      case 't':
        ret->push_back('\t');
        break;
      case '\\':
        ret->push_back('\\');
        break;
      case 'x':
        if (i < size - 2) {
          char c3 = s[++i];
          char c4 = s[++i];
          ret->push_back((char)((hex_int(c3) << 4) + hex_int(c4)));
        }
        break;
      default:
        ret->push_back(c2);
        break;
      }
    }
  }
}

inline static std::string str_unescape(const char *s, int size) {
  std::string ret;
  ret.reserve(size);
  str_unescape(s, size, &ret);
  return ret;
}

inline static std::string str_unescape(const std::string &s) {
  return str_unescape(s.data(), (int)s.size());
}

inline static std::string hexmem(const void *p, int size) {
  return str_escape((char *)p, size);
  /*
  std::string ret;
  char buf[4];
  for(int i=0; i<size; i++){
          char c = ((char *)p)[i];
          if(isalnum(c) || isprint(c)){
                  ret.append(1, c);
          }else{
                  switch(c){
                          case '\r':
                                  ret.append("\\r", 2);
                                  break;
                          case '\n':
                                  ret.append("\\n", 2);
                                  break;
                          default:
                                  sprintf(buf, "\\%02x", (unsigned char)c);
                                  ret.append(buf, 3);
                  }
          }
  }
  return ret;
  */
}

// TODO: mem_printf("%5c%d%s", p, size);
static inline void dump(const void *p, int size, const char *msg = NULL) {
  if (msg == NULL) {
    printf("dump <");
  } else {
    printf("%s <", msg);
  }
  std::string s = hexmem(p, size);
  printf("%s>\n", s.c_str());
}

static inline std::string str(const char *s) { return std::string(s); }

static inline std::string str(int v) {
  char buf[21] = {0};
  snprintf(buf, sizeof(buf), "%d", v);
  return std::string(buf);
}

static inline std::string str(int64_t v) {
  char buf[21] = {0};
  snprintf(buf, sizeof(buf), "%" PRId64 "", v);
  return std::string(buf);
}

static inline std::string str(uint64_t v) {
  char buf[21] = {0};
  snprintf(buf, sizeof(buf), "%" PRIu64 "", v);
  return std::string(buf);
}

static inline std::string str(double v) {
  char buf[21] = {0};
  if (v - floor(v) == 0) {
    snprintf(buf, sizeof(buf), "%.0f", v);
  } else {
    snprintf(buf, sizeof(buf), "%f", v);
  }
  return std::string(buf);
}

static inline std::string str(float v) { return str((double)v); }

// all str_to_xx methods set errno on error

static inline int str_to_int(const std::string &str) {
  const char *start = str.c_str();
  char *end;
  int ret = (int)strtol(start, &end, 10);
  // the WHOLE string must be string represented integer
  if (*end == '\0' && size_t(end - start) == str.size()) {
    errno = 0;
  } else {
    // strtoxx do not set errno all the time!
    if (errno == 0) {
      errno = EINVAL;
    }
  }
  return ret;
}

static inline int str_to_int(const char *p, int size) {
  return str_to_int(std::string(p, size));
}

static inline int64_t str_to_int64(const std::string &str) {
  const char *start = str.c_str();
  char *end;
  int64_t ret = (int64_t)strtoll(start, &end, 10);
  // the WHOLE string must be string represented integer
  if (*end == '\0' && size_t(end - start) == str.size()) {
    errno = 0;
  } else {
    // strtoxx do not set errno all the time!
    if (errno == 0) {
      errno = EINVAL;
    }
  }
  return ret;
}

static inline int64_t str_to_int64(const char *p, int size) {
  return str_to_int64(std::string(p, size));
}

static inline uint64_t str_to_uint64(const std::string &str) {
  const char *start = str.c_str();
  char *end;
  uint64_t ret = (uint64_t)strtoull(start, &end, 10);
  // the WHOLE string must be string represented integer
  if (*end == '\0' && size_t(end - start) == str.size()) {
    errno = 0;
  } else {
    // strtoxx do not set errno all the time!
    if (errno == 0) {
      errno = EINVAL;
    }
  }
  return ret;
}

static inline uint64_t str_to_uint64(const char *p, int size) {
  return str_to_uint64(std::string(p, size));
}

static inline double str_to_double(const char *p, int size) {
  return atof(std::string(p, size).c_str());
}

static inline std::string substr(const std::string &str, int start, int size) {
  if (start < 0) {
    start = (int)str.size() + start;
  }
  if (size < 0) {
    // 忽略掉 abs(size) 个字节
    size = ((int)str.size() + size) - start;
  }
  if (start < 0 || size_t(start) >= str.size() || size < 0) {
    return "";
  }
  return str.substr(start, size);
}

static inline std::string str_slice(const std::string &str, int start,
                                    int end) {
  if (start < 0) {
    start = (int)str.size() + start;
  }
  int size;
  if (end < 0) {
    size = ((int)str.size() + end + 1) - start;
  } else {
    size = end - start + 1;
  }
  if (start < 0 || size_t(start) >= str.size() || size < 0) {
    return "";
  }
  return str.substr(start, size);
}

static inline int bitcount(const char *p, int size) {
  int n = 0;
  for (int i = 0; i < size; i++) {
    unsigned char c = (unsigned char)p[i];
    while (c) {
      n += c & 1;
      c = c >> 1;
    }
  }
  return n;
}

// is big endia. TODO: auto detect
#if 0
#define big_endian(v) (v)
#else
static inline uint16_t big_endian(uint16_t v) { return (v >> 8) | (v << 8); }

static inline uint32_t big_endian(uint32_t v) {
  return (v >> 24) | ((v >> 8) & 0xff00) | ((v << 8) & 0xff0000) | (v << 24);
}

static inline uint64_t big_endian(uint64_t v) {
  uint32_t h = v >> 32;
  uint32_t l = v & 0xffffffffull;
  return big_endian(h) | ((uint64_t)big_endian(l) << 32);
}
#endif


}
#endif
